#include "tcpclient.h"

Tcpclient::Tcpclient(/* args */)
{
    this->clientfd = -1;
}

Tcpclient::~Tcpclient()
{
    this->close();
}

bool Tcpclient::connect(const std::string &ip, const unsigned short &port)
{
    if (this->clientfd != -1)   // 判断该客户端socket是否已连接
    {
        return false;
    }
    this->ip = ip;
    this->port = port;
    

    // 1、创建客户端的socket
    this->clientfd = socket(AF_INET, SOCK_STREAM, 0);
    if (this->clientfd == -1)
    {
        std::cout << "socket error" << std::endl;
        return -1;
    }

    // 2、向服务器发起连接请求
    struct hostent* h;   // 用于储存服务端ip的数据结构
    h = gethostbyname(this->ip.c_str());
    if (h == 0)
    {
        std::cout << "gethostbyname failed" << std::endl;
        ::close(this->clientfd);
        this->clientfd = -1;
        return false;
    }
    struct sockaddr_in serveraddr;  //存放服务端ip和端口的结构体
    memset(&serveraddr, 0, sizeof(serveraddr));
    serveraddr.sin_family = AF_INET;
    memcpy(&serveraddr.sin_addr, h->h_addr, h->h_length);  // 指定服务端的ip
    serveraddr.sin_port = htons(this->port);  // 指定服务端的port

    int connectId = ::connect(this->clientfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr));
    if (connectId != 0)
    {
        std::cout << "connect err!" << std::endl;
        ::close(this->clientfd);
        this->clientfd = -1;
        return false;
    }
    return true;
}

bool Tcpclient::send(const std::string &buffer)
{
    if (this->clientfd == -1)   // 判断该客户端socket是否已连接
    {
        return false;
    }
    
    int retIndex = ::send(this->clientfd, buffer.data(), buffer.size(), 0);
    if (retIndex <= 0)
    {
        return false;
    }
    return true;
}

bool Tcpclient::recv(std::string &buffer, const size_t maxLen)
{
    buffer.clear();  //清空容器
    buffer.resize(maxLen);   // 设置容器的大小maxLen
    int readNum = ::recv(this->clientfd, &buffer[0], buffer.size(), 0);  // 直接操作buffer的内存，一般不会直接操作string对象的内存，
                                                                        // 如果直接操作string对象的内存，1，不能越界,2，操作后手动设置数据大小
    if (readNum <= 0)  // -1失败，0 socket断开连接，>0成功接收到数据
    {
        buffer.clear();
        return false;
    }
    buffer.resize(readNum);   // 重新设置buffer的大小
    return true;
}

bool Tcpclient::close()
{
    if (this->clientfd == -1)
    {
        return false;   // socket未连接
    }
    ::close(this->clientfd);
    this->clientfd = -1;
    return true;
}

bool Tcpclient::send(void * buffer, const size_t size)
{
    if (this->clientfd == -1)
    {
        return false;
    }

    int retIndex = ::send(this->clientfd, buffer, size, 0);
    if (retIndex <= 0)
    {
        return false;
    }
    return true;
}

bool Tcpclient::sendFile(const std::string & fileName, const size_t fileSize)
{
    // 已二进制的方式打开文件
    std::ifstream ifs;
    ifs.open(fileName, std::ios::binary);
    if(!ifs.is_open())
    {
        std::cout << "打开文件" << fileName << "失败" << std::endl;
        return false;
    }
    int onread = 0;   // 每次调用ifs.read()时打算读取的字节数，每次搬的砖头数
    int readBytes = 0;   // 从文件中已读取的字节总数  已搬砖头数
    char buffer[7];    // 存放读取数据的buffer  每次搬的7块砖头

    while (true)
    {
        memset(buffer, 0, sizeof(buffer));

        // 计算本次应该读取的字节数，如果剩余的数据超过7字节，读取7字节
        if (fileSize - readBytes > 7)
        {
            onread = 7;
        }
        else{
            onread = fileSize - readBytes;
        }

        // 从文件中读取数据
        ifs.read(buffer, onread);
        
        // 把读取到的数据发送给对方
        int flag = this->send(buffer, onread);
        if(!flag)
        {
            return false;
        }

        // 计算文件已读取的字节总数，如果文件已读完，跳出循环
        readBytes = readBytes + onread;
        if (readBytes == fileSize)
        {
            break;
        }
        
    }
    return true;
    
}
